﻿using System.Collections.Generic;
using System.Linq;
using NHibernate.Linq;
using Xenta.Api;
using Xenta.Entities;
using Xenta.Enums;
using Xenta.Security;
using Xenta.Structs;
using Xenta.Utils;

namespace Xenta.Operations
{
    #region __General__

    /// <summary>
    /// Creates a file group.
    /// </summary>
    public class CreateFileGroup : CreateAuditable<FileGroupEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("FILESYS");
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .IsVerified()
                    .Rule((ctx, uow) =>
                    {
                        var owner = ctx.FileOwner(uow);
                        return owner != null &&
                            owner.Flags.HasFlag(FileOwnerFlags.Active) &&
                            owner.EntityID == OwnerID;
                    });
            }
        }

        /// <summary>
        /// Gets or sets the owner identifier.
        /// </summary>
        public int OwnerID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the flags.
        /// </summary>
        public FileGroupFlags Flags
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Construct(IUnitOfWork uow, FileGroupEntity entity)
        {
            base.Construct(uow, entity);

            entity.Owner = uow.Execute(new GetFileOwner
            {
                ThrowsException = true,
                EntityID = OwnerID
            }).Result;
            entity.Flags = Flags;
        }

        #endregion
    }

    /// <summary>
    /// Updates a file group.
    /// </summary>
    public class UpdateFileGroup : UpdateAuditable<FileGroupEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("FILESYS");
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .IsVerified()
                    .Rule((ctx, uow) =>
                    {
                        var owner = ctx.FileOwner(uow);
                        if(owner == null)
                            return false;
                        var group = uow.Execute(new GetFileGroup
                        {
                            EntityID = EntityID
                        }).Result;
                        if(group == null)
                            return false;
                        return group.Owner.EntityID == owner.EntityID;
                    });
            }
        }

        /// <summary>
        /// Gets or sets the flags.
        /// </summary>
        public FileGroupFlags Flags
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Revise(IUnitOfWork uow, FileGroupEntity entity)
        {
            base.Revise(uow, entity);

            entity.Flags = Flags;
        }

        #endregion
    }

    /// <summary>
    /// Deletes the file group.
    /// </summary>
    public class DeleteFileGroup : DeleteEntity<FileGroupEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("FILESYS");
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .IsVerified()
                    .Rule((ctx, uow) =>
                    {
                        var owner = ctx.FileOwner(uow);
                        if(owner == null)
                            return false;
                        var group = uow.Execute(new GetFileGroup
                        {
                            EntityID = EntityID
                        }).Result;
                        if(group == null)
                            return false;
                        return group.Owner.EntityID == owner.EntityID;
                    });
            }
        }

        /// <summary>
        /// Gets or sets a value indicates, whether 
        /// delete the group related files.
        /// </summary>
        public bool DeleteFiles
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Destruct(IUnitOfWork uow, FileGroupEntity entity)
        {
            foreach(var i in entity.Translations)
            {
                uow.Execute(new DeleteFileGroupTranslation
                {
                    EntityID = i.EntityID
                });
            }
            foreach(var i in entity.Files.ToList())
            {
                uow.Execute(new UnmapFileFromGroup
                {
                    ChildID = i.EntityID,
                    EntityID = entity.EntityID
                });
                if(DeleteFiles)
                {
                    uow.Execute(new DeleteFile
                    {
                        EntityID = i.EntityID
                    });
                }
            }
            base.Destruct(uow, entity);
        }

        #endregion
    }

    /// <summary>
    /// Gets the file group by identifier.
    /// </summary>
    public class GetFileGroup : QueryEntityByID<FileGroupEntity>,
        IApiOperation
    {
        #region Utilities

        protected override IQueryable<FileGroupEntity> Build(IUnitOfWork uow, IQueryable<FileGroupEntity> query)
        {
            query = base.Build(uow, query);
            if(Fetch > FetchLevel.None)
                query = query.Fetch(x => x.Owner)
                    .ThenFetch(x => x.Account);
            return query;
        }

        #endregion
    }

    #endregion

    #region __Translation__

    /// <summary>
    /// Creates a group translation.
    /// </summary>
    public class CreateFileGroupTranslation : CreateTranslation
        <TFileGroupEntity, FileGroupEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("FILESYS");
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .IsVerified()
                    .Rule((ctx, uow) =>
                    {
                        var owner = ctx.FileOwner(uow);
                        if(owner == null)
                            return false;
                        var group = uow.Execute(new GetFileGroup
                        {
                            EntityID = HolderID
                        }).Result;
                        if(group == null)
                            return false;
                        return group.Owner.EntityID == owner.EntityID;
                    });
            }
        }

        /// <summary>
        /// Gets or sets the name.
        /// </summary>
        public string Name
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Construct(IUnitOfWork uow, TFileGroupEntity entity)
        {
            base.Construct(uow, entity);

            entity.Name = StringHelper.EnsureNotNull(Name).TrimToLen(100);
        }

        #endregion
    }

    /// <summary>
    /// Updates the group translation.
    /// </summary>
    public class UpdateFileGroupTranslation : UpdateTranslation
        <TFileGroupEntity, FileGroupEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("FILESYS");
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .IsVerified()
                    .Rule((ctx, uow) =>
                    {
                        var owner = ctx.FileOwner(uow);
                        if(owner == null)
                            return false;
                        var translation = uow.Execute(new GetFileGroupTranslation
                        {
                            EntityID = EntityID
                        }).Result;
                        if(translation == null)
                            return false;
                        return translation.Holder.Owner.EntityID == owner.EntityID;
                    });
            }
        }

        /// <summary>
        /// Gets or sets the name.
        /// </summary>
        public string Name
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Revise(IUnitOfWork uow, TFileGroupEntity entity)
        {
            base.Revise(uow, entity);

            entity.Name = StringHelper.EnsureNotNull(Name).TrimToLen(100);
        }

        #endregion
    }

    /// <summary>
    /// Saves a group translation.
    /// </summary>
    public class SaveFileGroupTranslation : SaveTranslation
        <TFileGroupEntity, FileGroupEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("FILESYS");
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .IsVerified()
                    .Rule((ctx, uow) =>
                    {
                        var owner = ctx.FileOwner(uow);
                        if(owner == null)
                            return false;
                        var group = uow.Execute(new GetFileGroup
                        {
                            EntityID = HolderID
                        }).Result;
                        if(group == null)
                            return false;
                        return group.Owner.EntityID == owner.EntityID;
                    });
            }
        }

        /// <summary>
        /// Gets or sets the name.
        /// </summary>
        public string Name
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override void Work(IUnitOfWork uow, out TFileGroupEntity result)
        {
            var translation = uow.Execute(new GetFileGroupTranslationByLanguage
            {
                HolderID = HolderID,
                LanguageID = LanguageID
            }).Result;
            if(translation == null)
            {
                translation = uow.Execute(new CreateFileGroupTranslation
                {
                    HolderID = HolderID,
                    LanguageID = LanguageID,
                    Name = Name
                }).Result;
            }
            else
            {
                translation = uow.Execute(new UpdateFileGroupTranslation
                {
                    EntityID = translation.EntityID,
                    LanguageID = LanguageID,
                    Name = Name
                }).Result;
            }
            result = translation;
        }

        #endregion
    }

    /// <summary>
    /// Deletes the group translation.
    /// </summary>
    public class DeleteFileGroupTranslation : DeleteTranlsation
        <TFileGroupEntity, FileGroupEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("FILESYS");
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .IsVerified()
                    .Rule((ctx, uow) =>
                    {
                        var owner = ctx.FileOwner(uow);
                        if(owner == null)
                            return false;
                        var translation = uow.Execute(new GetFileGroupTranslation
                        {
                            EntityID = EntityID
                        }).Result;
                        if(translation == null)
                            return false;
                        return translation.Holder.Owner.EntityID == owner.EntityID;
                    });
            }
        }

        #endregion
    }

    /// <summary>
    /// Gets the group translation.
    /// </summary>
    public class GetFileGroupTranslation : GetTranlsation
        <TFileGroupEntity, FileGroupEntity>,
        IApiOperation
    {
    }

    /// <summary>
    /// Gets the group translation.
    /// </summary>
    public class GetFileGroupTranslationByLanguage : GetTranlsationByLanguage
        <TFileGroupEntity, FileGroupEntity>,
        IApiOperation
    {
    }

    #endregion

    #region __Search__

    /// <summary>
    /// Search over file group collection.
    /// </summary>
    public class SearchFileGroups : SearchOperation<FileGroupEntity>,
        IApiOperation
    {
        #region Ctors

        /// <summary>
        /// Initializes a new class instance.
        /// </summary>
        public SearchFileGroups()
        {
            Term = SearchTerm.Empty;
            SortDir = SortDirection.Ascending;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the search term.
        /// </summary>
        public SearchTerm Term
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the language identifier.
        /// </summary>
        public int? LanguageID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the owner identifier.
        /// </summary>
        public int? OwnerID
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the flag mask.
        /// </summary>
        public FileGroupFlags FlagMask
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the sort direction.
        /// </summary>
        public SortDirection SortDir
        {
            get;
            set;
        }

        #endregion

        #region Utilities

        protected override IQueryable<FileGroupEntity> Build(IUnitOfWork uow, IQueryable<FileGroupEntity> query)
        {
            query = base.Build(uow, query);
            query = query.Fetch(x => x.Owner).ThenFetch(x => x.Account);
            query = query.FilterByFlags(x => (int)x.Flags, (int)FlagMask);
            switch(Term.Mode)
            {
                case SearchTerm.SearchMode.StartsWith:
                    if(LanguageID.HasValue)
                        query = query.Where(x => x.Translations.Any(i =>
                            i.Language.EntityID == LanguageID.Value &&
                            i.Name.StartsWith(Term.Keyword)));
                    else
                        query = query.Where(x => x.Translations.Any(i =>
                            i.Name.StartsWith(Term.Keyword)));
                    break;
                case SearchTerm.SearchMode.EndsWith:
                    if(LanguageID.HasValue)
                        query = query.Where(x => x.Translations.Any(i =>
                            i.Language.EntityID == LanguageID.Value &&
                            i.Name.EndsWith(Term.Keyword)));
                    else
                        query = query.Where(x => x.Translations.Any(i =>
                            i.Name.EndsWith(Term.Keyword)));
                    break;
                case SearchTerm.SearchMode.Contains:
                    if(LanguageID.HasValue)
                        query = query.Where(x => x.Translations.Any(i =>
                            i.Language.EntityID == LanguageID.Value &&
                            i.Name.Contains(Term.Keyword)));
                    else
                        query = query.Where(x => x.Translations.Any(i =>
                            i.Name.Contains(Term.Keyword)));
                    break;
                case SearchTerm.SearchMode.Equals:
                    if(LanguageID.HasValue)
                        query = query.Where(x => x.Translations.Any(i =>
                            i.Language.EntityID == LanguageID.Value &&
                            i.Name.Equals(Term.Keyword)));
                    else
                        query = query.Where(x => x.Translations.Any(i =>
                            i.Name.Equals(Term.Keyword)));
                    break;
                default:
                    if(LanguageID.HasValue)
                        query = query.Where(x => x.Translations.Any(i => i.Language.EntityID == LanguageID.Value));
                    break;
            }
            if(OwnerID.HasValue)
                query = query.Where(x => x.Owner.EntityID == OwnerID.Value);
            query = query.OrderBy(x => x.EntityID, SortDir);
            return query;
        }

        #endregion
    }

    #endregion

    #region __Map__

    /// <summary>
    /// Maps the file to group.
    /// </summary>
    public class MapFileToGroup : MapOperation<FileGroupEntity, FileEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("FILESYS");
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .IsVerified()
                    .Rule((ctx, uow) =>
                    {
                        var owner = ctx.FileOwner(uow);
                        if(owner == null)
                            return false;
                        var file = uow.Execute(new GetFile
                        {
                            EntityID = ChildID
                        }).Result;
                        if(file == null)
                            return false;
                        var group = uow.Execute(new GetFileGroup
                        {
                            EntityID = EntityID
                        }).Result;
                        if(group == null)
                            return false;
                        return file.Owner.EntityID == owner.EntityID &&
                            group.Owner.EntityID == owner.EntityID;
                    });
            }
        }

        #endregion
    }

    /// <summary>
    /// Unmaps the file from group.
    /// </summary>
    public class UnmapFileFromGroup : UnmapOperation<FileGroupEntity, FileEntity>,
        ISecuredOperation, IApiOperation
    {
        #region Properties

        /// <summary>
        /// Gets the collection of security policies.
        /// </summary>
        public IEnumerable<SecurityPolicy> SecurityPolicies
        {
            get
            {
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .HasPermission("FILESYS");
                yield return new SecurityPolicy()
                    .IsAuthenticated()
                    .IsVerified()
                    .Rule((ctx, uow) =>
                    {
                        var owner = ctx.FileOwner(uow);
                        if(owner == null)
                            return false;
                        var file = uow.Execute(new GetFile
                        {
                            EntityID = ChildID
                        }).Result;
                        if(file == null)
                            return false;
                        var group = uow.Execute(new GetFileGroup
                        {
                            EntityID = EntityID
                        }).Result;
                        if(group == null)
                            return false;
                        return file.Owner.EntityID == owner.EntityID &&
                            group.Owner.EntityID == owner.EntityID;
                    });
            }
        }

        #endregion
    }

    #endregion
}
